﻿using BCCommon;
using BCMongoEntity.TMS.Location.Driver;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BCMongoData.TMS.Location.Driver
{
    public class DriverLocationMongoData : IDriverLocationMongoData
    {
        private MongoDBCommand<DriverLocationEntity> mongoCommand;
        private MongoDBCommand<BsonDocument> geoNaerCommand;

        public DriverLocationMongoData(IMongoDBConfiguration mongoDBConfiguration)
        {
            this.mongoCommand = new MongoDBCommand<DriverLocationEntity>(mongoDBConfiguration, "DriverLocation");
            this.geoNaerCommand = new MongoDBCommand<BsonDocument>(mongoDBConfiguration, "DriverLocation");
        }

        public DriverLocationEntity Update(DriverLocationEntity driver)
        {
            var builder = Builders<DriverLocationEntity>.Update.Set(x => x.Latitude, driver.Latitude)
                          .Set(y => y.Longitude, driver.Longitude)
                          .Set(y => y.WorkingStatus, driver.WorkingStatus)
                          .Set(y => y.Name, driver.Name)
                          .Set(y => y.Phone, driver.Phone)
                          .Set(y => y.VehicleNo, driver.VehicleNo)
                          .Set(y => y.VehicleTypeId, driver.VehicleTypeId)
                          .Set(y => y.VehicleType, driver.VehicleType)
                          .Set(y => y.VehicleLength, driver.VehicleLength)
                          .Set(y => y.VehicleWidth, driver.VehicleWidth)
                          .Set(y => y.VehicleHeight, driver.VehicleHeight)
                          .Set(y => y.DriverPhotoPath, driver.DriverPhotoPath)
                          .Set(y => y.ModifyTime, DateTime.Now)
                          .Set(y => y.Coordinate, new double[] { Math.Round(driver.Longitude, 8), Math.Round(driver.Latitude, 8) })
                          .Set(y => y.CityCode, driver.CityCode)
                          .Set(y => y.CoutyCode, driver.CoutyCode)
                          .Set(y => y.VehicleStere, driver.VehicleStere)
                          .Set(y => y.CreditScore, driver.CreditScore)
                          .Set(y => y.DailyReceivedOrders, driver.DailyReceivedOrders)
                          .Set(y => y.TotalReceivedOrders, driver.TotalReceivedOrders)
                          .Set(y => y.FullLoadRatio, driver.FullLoadRatio);
            this.mongoCommand.UpdateDocumentAsync(d => d.DriverId == driver.DriverId, builder).GetAwaiter().GetResult();
            return this.mongoCommand.SelectDocumentAsync(s => s.DriverId == driver.DriverId).GetAwaiter().GetResult();
        }


        public IEnumerable<DriverLocationNearEntity> GetGeoNearLocationsByCoordinate(double[] location, double maxDistance, List<BsonElement> query, List<BsonElement> projections)
        {
            List<BsonElement> elementList = new List<BsonElement>
            {
                new BsonElement("near", BsonValue.Create(new double[] { Math.Round(location[0], 8), Math.Round(location[1], 8) })),
                new BsonElement("distanceField", "dis"),
                new BsonElement("spherical", true),
                new BsonElement("maxDistance", maxDistance * 1000 / 6378137),
                new BsonElement("distanceMultiplier", 6378137)
            };
            if (query != null)
            {
                elementList.Add(new BsonElement("query", BsonValue.Create(new BsonDocument(query))));
            }           

            List<BsonDocument> bsonDocument = new List<BsonDocument>
            {
                new BsonDocument { { "$geoNear", new BsonDocument(elementList) } }
            };
            if (projections != null)
            {
                bsonDocument.Add(new BsonDocument { { "$project", new BsonDocument(projections) } });
            }

            var pipelineDefinition = PipelineDefinition<BsonDocument, DriverLocationNearEntity>.Create(bsonDocument.ToArray());

            var result = this.geoNaerCommand.Aggregate(pipelineDefinition).GetAwaiter().GetResult();
            return result.ToList();
        }

        public Task<DriverLocationEntity> GetDriverLocationInfo(string vehicleNo)
        {
            return this.mongoCommand.SelectDocumentAsync(p => p.VehicleNo == vehicleNo);
        }
    }
}
